/* mexUDPMxGram.c
 *
 * Matlab mex interface for using UDP/IP sockets and sending and
 * receiving 2D data of double, logical, char, or cell type, or 1D struct 
 * type.
 *
 *  2010
 *  benjamin.heasly@gmail.com
 *	University of Pennsylvania
 *
 */

#include "../mexUDP/mexUDP.h"
#include "../mxGram/mxGram.h"

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {
    
    int status = 0;
    int nBytes = 0;
    static char bigByteBuffer[MEXUDP_MAX_DATAGRAM_LENGTH];
    
    // First argument should be a command string
    if(nrhs >= 1 && mxIsChar(prhs[0]) && mxGetM(prhs[0])==1) {
        mxGetString(prhs[0], bigByteBuffer, sizeof(bigByteBuffer));
        
        // second argument may be a socketID;
        int sockID = -1;
        if(nrhs>=2 && mxIsNumeric(prhs[1]))
            sockID = (int)mxGetScalar(prhs[1]);
        
        if(!strcmp(bigByteBuffer, "open")) {
            
            mexAtExit(mexUDP_closeAll);

            if(sizeof(double) != 8) mexWarnMsgText("This platform does not use 8-byte doubles--a problem");
            
            //  IP address args are short strings
            //  PORT args are numbers.
            if(nrhs>=4 && mxIsNumeric(prhs[3])
            && mxIsChar(prhs[1]) && mxIsChar(prhs[2])) {

                char *localIP = mxArrayToString(prhs[1]);
                char *remoteIP = mxArrayToString(prhs[2]);
                int localPort = (int)mxGetScalar(prhs[3]);
                
                // remote port is optional
                int remotePort;
                if(nrhs==5 && mxIsNumeric(prhs[4]))
                    remotePort = (int)mxGetScalar(prhs[4]);
                else
                    remotePort = localPort;
                
                // try to reuse an existing, matching socket
                status = mexUDP_find(localIP, remoteIP, localPort, remotePort);
                
                if(status < 0)
                    status = mexUDP_open(localIP, remoteIP, localPort, remotePort);
                
                mxFree(localIP);
                mxFree(remoteIP);
                
            } else
                status = -10;
            
        } else if(!strcmp(bigByteBuffer, "send")) {
            
            if (mexUDP_isValidSocketIndex(sockID)) {
                
               // only send a 1xN char array
                if(nrhs==3) {

                    nBytes = mxToBytes(prhs[2], bigByteBuffer, sizeof(bigByteBuffer));
                    if (nBytes >= 0) {
                        status = mexUDP_send(sockID, bigByteBuffer, nBytes);
                        
                    } else 
                        status = nBytes;
                    
                } else
                    status = -30;
                
            } else
                status = -40;

        } else if(!strcmp(bigByteBuffer, "check")) {
            
            if (mexUDP_isValidSocketIndex(sockID))
                status = (int)(mexUDP_check(sockID) != 0);
            else
                status = -50;
            
        } else if(!strcmp(bigByteBuffer, "receive")) {
            
            if (mexUDP_isValidSocketIndex(sockID)) {
                
                if (mexUDP_check(sockID) != 0) {

                    nBytes = mexUDP_receive(sockID, bigByteBuffer, sizeof(bigByteBuffer));
                    if(nBytes > 0) {
                        mxArray *newMex;
                        status = bytesToMx(&newMex, (const char *)bigByteBuffer, nBytes);
                        plhs[0] = newMex;
                        
                    } else {
                        plhs[0] = mxCreateDoubleMatrix(0, 0, mxREAL);
                        status = -55;
                    }

                } else {
                    plhs[0] = mxCreateDoubleMatrix(0, 0, mxREAL);
                    status = 0;
                }
                
                if(nlhs > 1)
                    plhs[1] = mxCreateDoubleScalar(status);
                
                return;
                
            } else
                status = -60;
            
        } else if(!strcmp(bigByteBuffer, "close")) {
            
            if (mexUDP_isValidSocketIndex(sockID))
                status = mexUDP_close(sockID);
            else
                status = 0;
            
        } else if(!strcmp(bigByteBuffer, "closeAll")) {
            
            mexUDP_closeAll();
            status = 0;
            
        } else {
            
            status = -1000;
        }
        
        // all subcommands return int status
        // except receive, which returns above
        plhs[0] = mxCreateDoubleScalar((double)status);
        
    } else {
        mexPrintf("mexUDPMxGram usage:\n %s\n %s\n %s\n %s\n %s\n %s\n",
                "socketID = mexUDPMxGram('open', (string)localIP, (string)remoteIP, (int)localPort [,(int) remotePort])",
                "status = mexUDPMxGram('send', socketID, variable)",
                "messageIsAvailable = mexUDPMxGram('check', socketID)",
                "[variable, status] = mexUDPMxGram('receive', socketID)",
                "status = mexUDPMxGram('close', socketID)",
                "status = mexUDPMxGram('closeAll')");
        return;
    }
}